package colorpicker
{
	public class ColorUtil
	{
		//gets the exact middle point of the two colors
		public static function averageColors(color1:uint, color2:uint):uint
		{
			return ColorUtil.getColorMix(color1, color2, 0.5);
		}
		
		//percentage must be between 0 and 1
		public static function getPercentageOfColor(baseColor:uint, percentage:Number):uint
		{
			percentage = Math.min(1, Math.max(0, percentage));
			var red:Number = (baseColor & 0xff0000) >> 16;
			var green:Number = (baseColor & 0x00ff00) >> 8;
			var blue:Number = baseColor & 0x0000ff;
			
			return (red * percentage) << 16 | (green * percentage) << 8 | (blue * percentage);
		}
		
		//percentage must be between 0 and 1
		public static function getPercentageOfGradient(minColor:uint, maxColor:uint, percentage:Number):uint
		{
			percentage = Math.min(1, Math.max(0, percentage));
			var maxRGB:Array = Num2RGB(maxColor);
			var minRGB:Array = Num2RGB(minColor);
			
			var red:Number = minRGB[0] + ((maxRGB[0] - minRGB[0]) * percentage);
			var green:Number = minRGB[1] + ((maxRGB[1] - minRGB[1]) * percentage); 
			var blue:Number = minRGB[2] + ((maxRGB[2] - minRGB[2]) * percentage); 
			
			return (red << 16) | (green << 8) | blue;
		}
		
		//returns a color from a point between two colors
		public static function getColorMix(color1:uint, color2:uint, percentage:Number):uint
		{
			return ColorUtil.getPercentageOfColor(color1, 1 - percentage) + ColorUtil.getPercentageOfColor(color2, percentage);
		}
		
		public static function convertToRGBObject(hex:uint):Object
		{
			//bitwise is faster
			return {r: (hex & 0xff0000) >> 16, g: (hex & 0x00ff00) >> 8, b: hex & 0x0000ff};
			/*var rgb_obj = new Object();
			
			var r = Math.floor(hex / 65536);
			var g = Math.floor((hex - (r*65536)) / 256);
			var b = hex- (r*65536) - (g*256);
			
			rgb_obj.r = r;
			rgb_obj.g = g;
			rgb_obj.b = b;
			
			return rgb_obj;*/
		}
		
		//since we receive an object with r, g, and b parameters, we should take accept it too!
		public static function convertRGBObjectToColor(color:Object):uint
		{
			return ColorUtil.convertRGBToColor(color.r, color.g, color.b);
		}
		
		//modified by Josh so that values never go above 255
		public static function convertRGBToColor(r:int, g:int, b:int):uint
		{
			var hex:Number = (Math.min(255, r) * 65536) + (Math.min(255, g) * 256) + Math.min(255, b);
			return hex;
		}
		
		/**
		 *  Don't ask Josh about this function. Someone else wrote it for an earlier version of Xcelsius.
		 */
		public static function convertHexStringToColor(hexString:String):int
		{
			// doesn't handle "0xff0000";  // add later
			var invalid:Boolean = false;
		
			var hexVal:Number = 0;
			var hex:Number = 0;
			
			var numChars:int = hexString.length;
			for (var i:int = numChars; i > 0; i--) {
				var multiplier:Number = Math.pow(16, (numChars - i));
				
				var char:String = hexString.charAt(i-1);
				switch (char) {
					case "A":
					case "a":
						hexVal = 10;
						break;				
					case "B":
					case "b":
						hexVal = 11;
						break;				
					case "C":
					case "c":
						hexVal = 12;
						break;				
					case "D":
					case "d":
						hexVal = 13;
						break;
					case "E":
					case "e":
						hexVal = 14;
						break;								
					case "F":
					case "f":
						hexVal = 15;
						break;				
					default:
						hexVal = Number(char);
		
						if (isNaN(hexVal)) {	// invalid string
							invalid = true;
							i = 0;				// quit looping
						}
				}
		
				hex += hexVal * multiplier;
			}				
			
			if (invalid) {
				return -1;						// error out if not a hex string
			}
		
			return hex;
		}
		
		public static function convertToHexString(color:uint):String
		{
			var hex:String = "#";
			var rgb:Object = ColorUtil.convertToRGBObject(color);
			
			for each (var colorName:String in ["r", "g", "b"]) 
			{
				var primaryHex:String = rgb[colorName].toString(16);
				if (primaryHex.length == 1) 
					hex += "0";
				hex += primaryHex;
			} 	
			
			return hex;
		}
		
		public static function HSL2RGB(Hue:int, Sat:int, Lum:int):Array
		{
			//trace("HUE:"+Hue.toString()+"\tSAT:"+Sat.toString()+"\tLUM:"+Lum.toString());
			// HSV contains values scaled as in the color wheel:
			// that is, all from 0 to 255.
	
			// for ( this code to work, HSV.Hue needs
			// to be scaled from 0 to 360 (it//s the angle of the selected
			// point within the circle). HSV.Saturation and HSV.value must
			// be
			// scaled to be between 0 and 1.
	
			var h:Number;
			var s:Number;
			var l:Number;
	
			var r:Number = 0;
			var g:Number = 0;
			var b:Number = 0;
	
			
			// Scale Hue to be between 0 and 360. Saturation
			// and value scale to be between 0 and 1.
			h = Hue / 255 * 360 % 360;
			s = Sat / 255;
			l = Lum / 255;
	
			if (s == 0) {
				// If s is 0, all colors are the same.
				// This is some flavor of gray.
				r = l;
				g = l;
				b = l;
			} else {
				var p:Number;
				var q:Number;
				var t:Number;
	
				var fractionalSector:Number;
				var sectorNumber:int;//
				var sectorPos:Number;
	
				// The color wheel consists of 6 sectors.
				// Figure out which sector you//re in.
				sectorPos = h / 60;
				sectorNumber = (int) (Math.floor(sectorPos));
	
				// get the fractional part of the sector.
				// That is, how many degrees into the sector
				// are you?
				fractionalSector = sectorPos - sectorNumber;
	
				// Calculate values for the three axes
				// of the color.
				p = l * (1 - s);
				q = l * (1 - (s * fractionalSector));
				t = l * (1 - (s * (1 - fractionalSector)));
	
				// Assign the fractional colors to r, g, and b
				// based on the sector the angle is in.
				switch (sectorNumber) {
				case 0:
					r = l;
					g = t;
					b = p;
					break;
	
				case 1:
					r = q;
					g = l;
					b = p;
					break;
	
				case 2:
					r = p;
					g = l;
					b = t;
					break;
	
				case 3:
					r = p;
					g = q;
					b = l;
					break;
	
				case 4:
					r = t;
					g = p;
					b = l;
					break;
	
				case 5:
					r = l;
					g = p;
					b = q;
					break;
				}
			}
			// return an RGB structure, with values scaled
			// to be between 0 and 255.
			//return new RGB(r * 255, g * 255, b * 255);
			
			var retRGB:Array = new Array();	
			
			retRGB[3] = r * 255;					// Number
			retRGB[4] = g * 255;					// Number
			retRGB[5] = b * 255;					// Number	
			
			retRGB[0] = int(Math.round(retRGB[3]));	// 0-255
			retRGB[1] = int(Math.round(retRGB[4]));	// 0-255
			retRGB[2] = int(Math.round(retRGB[5]));	// 0-255	
			
			// trace("RED:"+retRGB[0].toString()+"\tGRE:"+retRGB[1].toString()+"\tBLU:"+retRGB[2].toString());
									
			return retRGB;

		}

		
		public static function RGB2HSL(R:int, G:int, B:int):Array
		{
			// trace("RED:"+R.toString()+"\tGRE:"+G.toString()+"\tBLU:"+B.toString());
			// In this function, R, G, and B values must be scaled
			// to be between 0 and 1.
			// HSV.Hue will be a value between 0 and 360, and
			// HSV.Saturation and value are between 0 and 1.
			// The code must scale these to be between 0 and 255 for
			// the purposes of this application.	
			var min:Number;
			var max:Number;
			var delta:Number;			
	
			var r:Number =  R / 255;
			var g:Number =  G / 255;
			var b:Number =  B / 255;
	
			var h:Number;
			var s:Number;
			var l:Number;
	
			
			min = Math.min(Math.min(r, g), b);
			max = Math.max(Math.max(r, g), b);
			l = max;
			delta = max - min;
			if (max == 0 || delta == 0) {
				// R, G, and B must be 0, or all the same.
				// In this case, S is 0, and H is undefined.
				// Using H = 0 is as good as any...
				s = 0;
				h = 0;
			} else {
				s = delta / max;
				if (r == max) {
					// Between Yellow and Magenta
					h = (g - b) / delta;
				} else if (g == max) {
					// Between Cyan and Yellow
					h = 2 + (b - r) / delta;
				} else {
					// Between Magenta and Cyan
					h = 4 + (r - g) / delta;
				}
	
			}
			// Scale h to be between 0 and 360.
			// This may require adding 360, if the value
			// is negative.
			h *= 60;
			if (h < 0) {
				h += 360;
			}
	
			// Scale to the requirements of this
			// application. All values are between 0 and 255.
			
			var retHSL:Array = new Array(6);			
			retHSL[3] = h / 360 * 255;				// Number
			retHSL[4] = s * 255;					// Number
			retHSL[5] = l * 255;					// Number
			
			retHSL[0] = int(Math.round(retHSL[3]));	// 0-255
			retHSL[1] = int(Math.round(retHSL[4]));	// 0-255
			retHSL[2] = int(Math.round(retHSL[5]));	// 0-255
			
			//trace("HUE:"+retHSL[0].toString()+"\tSAT:"+retHSL[1].toString()+"\tLUM:"+retHSL[2].toString());
						
			return retHSL;
		}
		
		public static function Num2HSL(color:Number):Array
		{
			var red:int = (color & 0xff0000) >> 16;
			var green:int = (color & 0x00ff00) >> 8;
			var blue:int = color & 0x0000ff;
			return RGB2HSL( red, green, blue );
			
		} 	
		public static function Num2RGB(color:Number):Array
		{
			return [(color & 0xff0000) >> 16, (color & 0x00ff00) >> 8, color & 0x0000ff] ;			
		} 
		
		public static function RGB2Num(r:Number, g:Number, b:Number):Number
		{
			var s:Number = r << 16 | g << 8 | b;
			return s;		
		}
		
		public static function HSL2Num(H:Number, S:Number, B:Number):Number
		{
			var Colors:Array = HSL2RGB( H, S, B);
			return RGB2Num( Colors[0], Colors[1], Colors[2]);		
		}
		
		public static function invert(color:uint):uint
		{
			var red:uint = (color >> 16);// & 0xFF;
			var green:uint = (color >> 8) & 0xFF;
			var blue:uint = color & 0xFF;
			
			red = 255 - red;
			green = 255 - green;
			blue = 255 - blue;    		
			
			return red << 16 | green << 8 | blue;
		}
	}
}